#!/usr/bin/env python
#
#       Server.py
#       
#       Copyright 2009 Ben Davis <its.hip@gmail.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

import pickle
import random
import socket
import sys
import threading
import time
import zlib

import Maps
import Orders
import Parser
import Players
import Ships
import TestMap as Map
import ToClient
import ToServer

class Clients(list):
    def send(self, data):
        for client in self:
            client.send(data)
    
    def close(self):
        for client in self:
            client.socket.close()
            del(self[0])
            
class Client:
    def __init__(self, sock, player):
        self.socket = sock
        self.socket.settimeout(0.01)
        self.player = player
    
    def send(self, data):
        # Pickle, compress and send data to client
        print "Sending:", data.__class__.__name__
        self.socket.send(zlib.compress(pickle.dumps(data), 9))
        time.sleep(0.01)
        #self.socket.send(pickle.dumps(data))
        
    def recv(self, buffer_size):
        data = self.socket.recv(buffer_size)
        if data != '':
            data = pickle.loads(zlib.decompress(data))
            print "Received:", data.__class__.__name__
            return data
        return None

def main():
    try:
        # Try to connect to lobby server
        #lobby = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        #lobby.connect(('localhost', 7060))
        
        HOST = ''
        try:
            PORT = int(sys.argv[1])
        except IndexError:
            PORT = 7059

        print "Starting server on port", PORT
        listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        listener.bind((HOST, PORT))
        listener.listen(1)
        listener.settimeout(0.01)
        
        players = Players.Players()
        map = Maps.Map(Map)
        clients = Clients()
        
        while True:
            try:
                # Listen for a new client
                s, address = listener.accept()
                # Add a new player for client. For the moment, name them
                # by IP address
                new_player = Players.Player(address[0])
                new_player.number = len(players)
                players.append(new_player)
                # Give them a ship to play with
                #while len(new_player.ships.values()) < 1:
                    #x, y = random.randint(0, Map.WIDTH - 1), random.randint(0, Map.HEIGHT - 1)
                    #for player in players.values():
                        #for ship in player.ships.values():
                            #if ship.x == x and ship.y == y:
                                #break
                    #else:
                        #new_player.ships.append(Ships.Pawn(new_player, x, y, random.randint(0, 7)))
                if len(players) == 1:
                    for x, y in ((4, 2), (8, 2), (12, 2), (16, 2), (20, 2)):
                        new_player.ships.append(Ships.Pawn(new_player, x, y, 4))
                    new_player.ships.append(Ships.Knight(new_player, 6, 8, 4))
                if len(players) == 2:
                    for x, y in ((4, 23), (8, 23), (12, 23), (16, 23), (20, 23)):
                        new_player.ships.append(Ships.Pawn(new_player, x, y, 0))
                # If this is the first player
                if len(players) == 1:
                    # It's their turn
                    players.turn = new_player.key()
                    for ship in players[players.turn].ships.values():
                        ship.steam += ship.steam_prod
                        if ship.steam > ship.steam_max:
                            ship.steam = ship.steam_max
                # Create a new client object for new connection
                client = Client(s, new_player)
                client.parser = Parser.Parser(client, clients, players)
                # Notify existing clients of the new player
                clients.send(ToClient.AddPlayer(client.player))
                # Add to the list of clients
                clients.append(client)
                print address, "connected"
        
                # Sync players/map data with new client
                client.send(ToClient.Sync(client.player, players, map))
                
            except (socket.error, socket.timeout):
                pass
                
            # Receive data from clients
            for client in clients[:]:
                try:
                    data = client.recv(1024)
                    if data is None:
                        # Change to next player
                        clients.remove(client)
                        del(players[client.player.key()])
                        if len(players) > 0:
                            clients.send(ToClient.RemovePlayer(client.player))
                            players.next()
                            clients.send(ToClient.NewTurn(players.turn))
                            for ship in players[players.turn].ships.values():
                                ship.steam += ship.steam_prod
                                if ship.steam > ship.steam_max:
                                    ship.steam = ship.steam_max
                    else:
                        data(client, clients, players)
                except socket.timeout:
                    pass
                except socket.error:
                    if len(players) > 0:
                        # Change to next player
                        clients.remove(client)
                    del(players[client.player.key()])
                    if len(players) > 0:
                        clients.send(ToClient.RemovePlayer(client.player))
                        players.next()
                        clients.send(ToClient.NewTurn(players.turn))
                        for ship in players[players.turn].ships.values():
                            ship.steam += ship.steam_prod
                            if ship.steam > ship.steam_max:
                                ship.steam = ship.steam_max
                    
            
            
    except (KeyboardInterrupt, SystemExit):
        listener.close()
        clients.close()
        
        
if __name__ == "__main__": main()
